﻿namespace Cephei
  open System
  /// Functional interface to QL121 factories
  module Cell =

      let Vector (a : Core.Generic.ICoCell<'a> seq) = 
        new Core.CoCellVector<'a> (a)

      let Matrix (m : Core.Generic.ICoCell<'m> seq seq) =
        new Core.CoCellMatrix<'m> (m)

      let Cube (c : Core.Generic.ICoCell<'c> seq seq seq) = 
        new Core.CoCellCube<'c> (c)
        
      /// Implements the Cell monad to create Cells from computational expressions
      type CellBuilder () =

          member this.Delay d = d
          member this.Return a = a        
          member this.Yield a = a
          member this.Run (p : unit -> 'T)  =
              Cephei.Core.CellFactory.Instance.CreateFunc p

          member this.Value p =
              Cephei.Core.CellFactory.Instance.Create (p)
          member this.Vector v = Vector v
          member this.Matrix m = Matrix m
          member this.Cube c = Cube c

      /// CellProxy fakes a cell for quasi-inline cells within a model
      type CellProxy<'T> (t : unit -> 'T) =
          let _p = t
          member this.Value = _p ()
          member this.Choice = 
              try
                 Cephei.Core.DataChoice<'T, string, System.Exception>.NewChoice1Of3 (_p())
              with 
              | ex -> Cephei.Core.DataChoice<'T, string, System.Exception>.NewChoice3Of3 (ex)

      /// Builder for CellProxy cells
      type CellProxyBuilder () = 
          member this.Return r = r
          member this.Delay d = d
          member this.Run (f : unit -> 'T) = CellProxy (f)
          member this.Vector v = Vector v
          member this.Matrix m = Matrix m
          member this.Cube c = Cube c

      // <summary>object for "let a = cell { return b.Value * c.Value }" </summary>
      let cell = new CellBuilder ()
      let cellProxy = CellProxyBuilder ()

      // convert a nullable struct into a F# option
      let OptionOfNullable (n : Nullable<'t>) = 
         if n.HasValue then 
            Some n.Value          
         else
            None

      // convert a reference object into a F# option
      let OptionOfObject (o : obj) =
         if o = null then
            None
         else
            Some o
